package com.ziven.common.widget.index;

import android.view.View;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.ziven.easygo.util.BiCarry;
import com.ziven.easygo.util.EasyUtils;
import com.ziven.easygo.util.LazyInit;
import com.ziven.easygo.util.LogHelper;
import com.ziven.easygo.util.Nulls;
import com.ziven.easygo.util.Obtain;

import java.util.List;

class IndexCallbackImpl extends RecyclerView.OnScrollListener implements IndexCallback {

    private static final String TAG = "IndexCallbackImpl";
    private RecyclerView recyclerView;
    private boolean isShouldScroll = false;
    private int toPosition = RecyclerView.NO_POSITION;
    private final Obtain<List<FastScrollBlock>> fastScrollBlockListObtain;
    private final LazyInit<ViewCache> viewCache = LazyInit.lazy(ViewCache::new);
    private final BiCarry<Boolean, View> viewSelectCarry;
    private FastScrollBlock touchingBlock;

    public IndexCallbackImpl(@NonNull Obtain<List<FastScrollBlock>> obtain,
                             @NonNull BiCarry<Boolean, View> carry) {
        fastScrollBlockListObtain = obtain;
        viewSelectCarry = carry;
    }

    public void setRecyclerView(@NonNull RecyclerView rv) {
        recyclerView = rv;
        rv.addOnScrollListener(this);
        rv.setRecyclerListener(viewCache.value());
        rv.addOnChildAttachStateChangeListener(viewCache.value());
    }

    @Override
    public void onScrollStateChanged(@NonNull RecyclerView rv, int newState) {
        if(RecyclerView.SCROLL_STATE_IDLE != newState) {
            return;
        }
        if (isShouldScroll) {
            isShouldScroll = false;
            smoothMoveToPosition(recyclerView, toPosition);
        }
        if(recyclerView != null) {
            recyclerView.post(this::viewSelectCarry);
        }
    }

    private void smoothMoveToPosition(RecyclerView rv, int position) {
        int[] positions = findVisibleItemPositions(rv);
        LogHelper.log(TAG + " positions[0]:" + positions[0] + " position:" + position + " positions[1]:" + positions[1]);
        if(positions[0] == RecyclerView.NO_POSITION || positions[1] == RecyclerView.NO_POSITION) {
            return;
        }
        rv.stopScroll();
        if (position <= positions[0]) {
            LogHelper.log(TAG + " Step 1:smoothScrollToPosition() position=" + position);
            rv.smoothScrollToPosition(position);
            return;
        }
        if (position <= positions[1]) {
            int distance = calculateScrollDistance(rv, position);
            LogHelper.log(TAG + " Step 2:smoothScrollBy() position=" + position + " distance=" + distance);
            if(distance != 0) {
                rv.smoothScrollBy(0, distance);
            }
            return;
        }
        LogHelper.log(TAG + " Step 3:smoothScrollToPosition() position=" + position);
        rv.smoothScrollToPosition(position);
        isShouldScroll = true;
    }

    @Override
    public void startUpdateIndexName(int top, int bottom) {
        LogHelper.log(TAG + " startUpdateIndexName()");
        toPosition = RecyclerView.NO_POSITION;
    }

    private int calculateScrollDistance(RecyclerView rv, int targetPosition) {
        RecyclerView.LayoutManager lm;
        if(rv == null || (lm = rv.getLayoutManager()) == null) {
            return 0;
        }
        View targetView = lm.findViewByPosition(targetPosition);
        if(targetView == null) {
            LogHelper.log(TAG + " Step 2:calculateScrollDistance() targetView is null.");
            return 0;
        }
        int paddingTop = rv.getPaddingTop();
        int targetTop = targetView.getTop();
        LogHelper.log(TAG + " Step 2:calculateScrollDistance() targetTop=" + targetTop + " paddingTop=" + paddingTop);
        return targetTop - paddingTop;
    }

    @NonNull
    private int[] findVisibleItemPositions(RecyclerView rv) {
        int[] positions = {RecyclerView.NO_POSITION, RecyclerView.NO_POSITION};
        if(rv == null) {
            return positions;
        }
        RecyclerView.LayoutManager lm = rv.getLayoutManager();
        if(!(lm instanceof LinearLayoutManager)) {
            return positions;
        }
        LinearLayoutManager llm = (LinearLayoutManager) lm;
        positions[0] = llm.findFirstVisibleItemPosition();
        positions[1] = llm.findLastVisibleItemPosition();
        return positions;
    }

    @Override
    public void updateIndexName(int index, @NonNull String name, int x, int y) {
        toPosition = findFastScrollBlockPosition(index, name);
        if(recyclerView != null && toPosition != RecyclerView.NO_POSITION) {
            touchingBlock = FastScrollBlock.newBlock(toPosition, name, index);
            smoothMoveToPosition(recyclerView, toPosition);
            recyclerView.post(this::viewSelectCarry);
        }
    }

    @Override
    public void endUpdateIndexName() {
        LogHelper.log(TAG + " endUpdateIndexName()");
        touchingBlock = null;
        viewCache.value().forEach(view -> viewSelectCarry.carry(null, view));
    }

    private int findFastScrollBlockPosition(int index, String indexName) {
        for(FastScrollBlock block : fastScrollBlockListObtain.obtain()) {
            if(block.isIndex(index, indexName)) {
                return block.position;
            }
        }
        return RecyclerView.NO_POSITION;
    }

    private void viewSelectCarry() {
        LogHelper.log(TAG + " viewSelectCarry():" + touchingBlock);
        if(recyclerView != null) {
            List<FastScrollBlock> list = fastScrollBlockListObtain.obtain();
            viewCache.value().forEach(view -> {
                int position = recyclerView.getChildAdapterPosition(view);
                LogHelper.log(TAG + " viewSelectCarry():" + position);
                Nulls.of(EasyUtils.getValue(list, position))
                        .doNotNull(block -> viewSelectCarry.carry(touchingBlock == null || block.isIndex(touchingBlock.index, touchingBlock.indexName), view))
                        .doNull(() -> viewSelectCarry.carry(false, view));
            });
        }
    }
}
